package com.myk.game.gobangchess.rooms;

import com.myk.game.gobangchess.DataManager;
import com.myk.game.gobangchess.constants.BoardStyleData;
import com.myk.game.gobangchess.constants.ColorCost;
import com.myk.game.gobangchess.constants.WinMode;
import com.myk.game.gobangchess.signs.BadgeSign;
import com.myk.game.gobangchess.signs.GroundSign;
import com.myk.game.gobangchess.utils.GridsUtils;
import com.myk.game.gobangchess.Room;
import com.myk.game.gobangchess.signs.AroundSign;
import com.myk.game.gobangchess.signs.LineSign;

import java.awt.*;
import java.util.*;
import java.util.List;

/**
 * 形棋
 */
public class ShapeRoom extends Room {

    private ActionState actionState; //当前行动状态
    private final int maxScore = 4; //获胜需要的最大分数
    private Point tempPoint; //切换到移子挤子状态时记录的临时坐标
    private GroundSign fallTip; //最新落子提示信息
    private LineSign arrowTip; //移动箭头提示信息s
    private List<ChessInfo> pieces; //棋盘上的棋子信息集合
    private List<List<ChessInfo>> symmetryPieces; //形成了对称的棋子信息集合

    public ShapeRoom(String roomName) {
        super(roomName, "\t\t日出设计的形棋。\n——赏心悦目，变化无穷。", "形棋",BoardStyleData.getGoBangStyle(15,15), true, true);
    }

    @Override
    public String getRuleText() {
        return "黑白双方轮流行动：\n" +
                "选择A：落子一枚棋子到空格\n" +
                "选择B：移动一枚己方棋子到任意空格\n" +
                "选择C：落子占据对方一枚棋子的位置，该棋子相邻位置必须要有空格，然后将对方棋子推到相邻空格\n" +
                "行动后结算棋形：若走棋后己方棋子形成至少6枚相邻棋子组成的轴对称图形，移除该对称图并获得分数，然后可在行动一次。\n" +
                "注意事项一：该棋的相邻包括上下左右和斜角的邻接格子。\n" +
                "注意事项二：轴对称图形的对称轴由整块相邻棋子决定，横竖斜向都行。\n" +
                "注意事项三：获得分数的计算方式为对称图形中（棋子数量-5）。\n" +
                "获胜条件：得分后，总分数大于等于4分。\n" +
                "和棋条件：双方皆无棋可走。";
    }

    @Override
    public void resetGrids() {
        step = 0;
        blackScore = 0;
        whiteScore = 0;
        gameTip = "-1";
        actionState = null;
        tempPoint = null;
        fallTip = null;
        arrowTip = null;
        pieces = new ArrayList<>();
        symmetryPieces = new ArrayList<>();
        curGrids = GridsUtils.copyGrids(originGrids);
        signList.clear();
    }

    @Override
    protected void onStartPrepare() {
        changeState(ActionState.WAIT_FALL_BLACK);
        noticeBlackMove();
    }

    @Override
    public String getGameInfo() {
        curGrids = GridsUtils.copyGrids(originGrids);
        signList.clear();
        //绘制棋子
        if (pieces != null && pieces.size() > 0) {
            for (ChessInfo piece : pieces) {
                curGrids[piece.row][piece.col] = piece.isBlack ? 1 : 2;
            }
        }

        //绘制提示标记
        if (fallTip != null) {
            signList.add(fallTip);
            //绘制白子可被挤到的位置
            if (checkState(ActionState.PUSH_AWAY_WHITE)) {
                int pRow = tempPoint.y;
                int pCol = tempPoint.x;
                int maxRow = curGrids.length - 1;
                int maxCol = curGrids[0].length - 1;
                signList.add(new BadgeSign(getIndex(tempPoint.y, tempPoint.x), "p", 2, "#00FFFFFF", ColorCost.DETAINED_BADGE_BLACK));
                List<Integer> signIds = new ArrayList<>();
                if (pRow > 0 && pCol > 0 && curGrids[pRow - 1][pCol - 1] == 0) {
                    signIds.add(getIndex(pRow - 1, pCol - 1));
                }
                if (pRow > 0 && curGrids[pRow - 1][pCol] == 0) {
                    signIds.add(getIndex(pRow - 1, pCol));
                }
                if (pRow > 0 && pCol < maxCol && curGrids[pRow - 1][pCol + 1] == 0) {
                    signIds.add(getIndex(pRow - 1, pCol + 1));
                }
                if (pCol > 0 && curGrids[pRow][pCol - 1] == 0) {
                    signIds.add(getIndex(pRow, pCol - 1));
                }
                if (pCol < maxCol && curGrids[pRow][pCol + 1] == 0) {
                    signIds.add(getIndex(pRow, pCol + 1));
                }
                if (pRow < maxRow && pCol > 0 && curGrids[pRow + 1][pCol - 1] == 0) {
                    signIds.add(getIndex(pRow + 1, pCol - 1));
                }
                if (pRow < maxRow && curGrids[pRow + 1][pCol] == 0) {
                    signIds.add(getIndex(pRow + 1, pCol));
                }
                if (pRow < maxRow && pCol < maxCol && curGrids[pRow + 1][pCol + 1] == 0) {
                    signIds.add(getIndex(pRow + 1, pCol + 1));
                }
                signList.add(new AroundSign(new ArrayList<>(signIds), "#4411EE11", "#FF00FF00", 1.0f));
            }
            //绘制黑子可被挤到的位置
            else if (checkState(ActionState.PUSH_AWAY_BLACK)) {
                int pRow = tempPoint.y;
                int pCol = tempPoint.x;
                int maxRow = curGrids.length - 1;
                int maxCol = curGrids[0].length - 1;
                signList.add(new BadgeSign(getIndex(tempPoint.y, tempPoint.x), "p", 2, "#00FFFFFF", ColorCost.DETAINED_BADGE_WHITE));
                List<Integer> signIds = new ArrayList<>();
                if (pRow > 0 && pCol > 0 && curGrids[pRow - 1][pCol - 1] == 0) {
                    signIds.add(getIndex(pRow - 1, pCol - 1));
                }
                if (pRow > 0 && curGrids[pRow - 1][pCol] == 0) {
                    signIds.add(getIndex(pRow - 1, pCol));
                }
                if (pRow > 0 && pCol < maxCol && curGrids[pRow - 1][pCol + 1] == 0) {
                    signIds.add(getIndex(pRow - 1, pCol + 1));
                }
                if (pCol > 0 && curGrids[pRow][pCol - 1] == 0) {
                    signIds.add(getIndex(pRow, pCol - 1));
                }
                if (pCol < maxCol && curGrids[pRow][pCol + 1] == 0) {
                    signIds.add(getIndex(pRow, pCol + 1));
                }
                if (pRow < maxRow && pCol > 0 && curGrids[pRow + 1][pCol - 1] == 0) {
                    signIds.add(getIndex(pRow + 1, pCol - 1));
                }
                if (pRow < maxRow && curGrids[pRow + 1][pCol] == 0) {
                    signIds.add(getIndex(pRow + 1, pCol));
                }
                if (pRow < maxRow && pCol < maxCol && curGrids[pRow + 1][pCol + 1] == 0) {
                    signIds.add(getIndex(pRow + 1, pCol + 1));
                }
                signList.add(new AroundSign(new ArrayList<>(signIds), "#4411EE11", "#FF00FF00", 1.0f));
            }
        }
        //绘制移动提示
        if (arrowTip != null) {
            signList.add(arrowTip);
        }

        //绘制成型范围标记
        if (symmetryPieces != null && symmetryPieces.size() > 0) {
            List<Integer> signIds = new ArrayList<>();
            for (List<ChessInfo> symmetryPiece : symmetryPieces) {
                for (ChessInfo chessInfo : symmetryPiece) {
                    signIds.add(getIndex(chessInfo.row, chessInfo.col));
                }
            }
            signList.add(new AroundSign(new ArrayList<>(signIds), "#66FFDD33", "#FFFFDD00", 1.2f));
        }
        return super.getGameInfo();
    }

    @Override
    public void doRule(int row, int col, int value) {
        processFallAction(col, row);
    }

    //处理落子行为
    private void processFallAction(int pCol, int pRow) {
        //根据坐标获取该位置的信息，为null就是空位置，不为空就是已有棋子了
        ChessInfo chessInfo = getChessByPoint(pCol, pRow);
        arrowTip = null; //默认清除箭头提示
        //等待黑棋落子
        if (checkState(ActionState.WAIT_FALL_BLACK)) {
            //落在空白处
            if (chessInfo == null) {
                fallPiece(true, pCol, pRow); //落下黑棋子
                setFallTip(pCol, pRow); //添加落子提示
                //黑棋成型了
                if (checkSymmetryShape(true)) {
                    changeState(ActionState.REMOVE_SHAPED_BLACK);//切换到黑棋快捷提子状态
                }
                //黑棋没成型
                else {
                    changeState(ActionState.WAIT_FALL_WHITE);//自动交换白棋行棋
                }
            }
            //落在有子处
            else {
                setFallTip(pCol, pRow); //添加落子提示
                setTempPoint(pCol, pRow); //记录落子位置
                //落子已有的黑棋处，可以移动黑棋
                if (chessInfo.isBlack) {
                    pieces.remove(chessInfo); //移除该棋子
                    changeState(ActionState.THEN_MOVE_BLACK); //切换到移动黑子的状态
                }
                //落在已有的白棋处
                else {
                    if (checkHasQi(pCol, pRow)) {
                        chessInfo.isBlack = true; //白棋变为黑棋
                        changeState(ActionState.PUSH_AWAY_WHITE); //切换到挤走白子的状态
                    }
                }
            }
        }
        //等待白棋落子
        else if (checkState(ActionState.WAIT_FALL_WHITE)) {
            //落在空白处
            if (chessInfo == null) {
                fallPiece(false, pCol, pRow); //落下白棋子
                setFallTip(pCol, pRow); //添加落子提示
                //白棋成型了
                if (checkSymmetryShape(false)) {
                    changeState(ActionState.REMOVE_SHAPED_WHITE);//切换到白棋快捷提子状态
                }
                //白棋没成型
                else {
                    changeState(ActionState.WAIT_FALL_BLACK);//自动交换黑棋行棋
                }
            }
            else {
                setFallTip(pCol, pRow); //添加落子提示
                setTempPoint(pCol, pRow); //记录落子位置
                //落子已有的白棋处，可以移动白棋
                if (!chessInfo.isBlack) {
                    pieces.remove(chessInfo); //移除该棋子
                    changeState(ActionState.THEN_MOVE_WHITE); //切换到移动白子的状态
                }
                //落在已有的黑棋处
                else {
                    if (checkHasQi(pCol, pRow)) {
                        chessInfo.isBlack = false; //黑棋变为白棋
                        changeState(ActionState.PUSH_AWAY_BLACK); //切换到挤走黑子的状态
                    }
                }
            }
        }
        //等待黑方移动黑棋
        else if (checkState(ActionState.THEN_MOVE_BLACK)) {
            //移动到空白位置
            if (chessInfo == null) {
                //移动到不同位置
                if (tempPoint.x != pCol || tempPoint.y != pRow) {
                    fallPiece(true, pCol, pRow); //移动黑棋到此处
                    setFallTip(tempPoint.x, tempPoint.y); //添加上一步落子提示
                    setArrowTip(pCol, pRow); //添加箭头提示
                    //黑棋成型了
                    if (checkSymmetryShape(true)) {
                        changeState(ActionState.REMOVE_SHAPED_BLACK);//切换到黑棋快捷提子状态
                    }
                    //黑棋没成型
                    else {
                        changeState(ActionState.WAIT_FALL_WHITE);//自动交换白棋行棋
                    }
                }
                //移动到原位置
                else {
                    //iGameUI.toast("不能移动到这个位置"); //提示用户不能这么做
                    setFallTip(tempPoint.x, tempPoint.y); //还原聚焦提示
                }
            }
            //移动的位置有棋
            else {
                //iGameUI.toast("不能移动到这个位置"); //提示用户不能这么做
                setFallTip(tempPoint.x, tempPoint.y); //还原落子提示
            }
        }
        //等待白方移动白棋
        else if (checkState(ActionState.THEN_MOVE_WHITE)) {
            //移动到空白位置
            if (chessInfo == null) {
                //移动到不同位置
                if (tempPoint.x != pCol || tempPoint.y != pRow) {
                    fallPiece(false, pCol, pRow); //移动白棋到此处
                    setFallTip(tempPoint.x, tempPoint.y); //添加上一步落子提示
                    setArrowTip(pCol, pRow); //添加箭头提示
                    //白棋成型了
                    if (checkSymmetryShape(false)) {
                        changeState(ActionState.REMOVE_SHAPED_WHITE);//切换到白棋快捷提子状态
                    }
                    //白棋没成型
                    else {
                        changeState(ActionState.WAIT_FALL_BLACK);//自动交换黑棋行棋
                    }
                }
                //移动到原位置
                else {
                    //iGameUI.toast("不能移动到这个位置"); //提示用户不能这么做
                    setFallTip(tempPoint.x, tempPoint.y); //还原聚焦提示
                }
            }
            //移动的位置有棋
            else {
                //iGameUI.toast("不能移动到这个位置"); //提示用户不能这么做
                setFallTip(tempPoint.x, tempPoint.y); //还原落子提示
            }
        }
        //等待黑方挤走白棋的位置
        else if (checkState(ActionState.PUSH_AWAY_WHITE)) {
            //挤走位置相邻
            if (Math.abs(tempPoint.x - pCol) < 2 && Math.abs(tempPoint.y - pRow) < 2) {
                //放到空白位置
                if (chessInfo == null) {
                    fallPiece(false, pCol, pRow); //把白棋挤到这个位置
                    setFallTip(tempPoint.x, tempPoint.y); //添加上一步的落子提示
                    setArrowTip(pCol, pRow); //添加箭头提示
                    //判断黑棋成型了
                    if (checkSymmetryShape(true)) {
                        changeState(ActionState.REMOVE_SHAPED_BLACK);//切换到黑棋快捷提子状态
                    }
                    //黑棋没成型
                    else {
                        changeState(ActionState.WAIT_FALL_WHITE);//自动交换白棋行棋
                    }
                }
                //移动的位置有棋
                else {
                    //iGameUI.toast("只能挤到无棋的位置"); //提示用户不能这么做
                    setFallTip(tempPoint.x, tempPoint.y); //还原落子提示
                }
            }
            //挤走的位置不相邻
            else {
                //iGameUI.toast("只能挤到相邻一圈的位置"); //提示用户不能这么做
                setFallTip(tempPoint.x, tempPoint.y); //还原落子提示
            }
        }
        //等待白方挤走黑棋的位置
        else if (checkState(ActionState.PUSH_AWAY_BLACK)) {
            //挤走位置相邻
            if (Math.abs(tempPoint.x - pCol) < 2 && Math.abs(tempPoint.y - pRow) < 2) {
                //放到空白位置
                if (chessInfo == null) {
                    fallPiece(true, pCol, pRow); //把黑棋挤到这个位置
                    setFallTip(tempPoint.x, tempPoint.y); //添加上一步的落子提示
                    setArrowTip(pCol, pRow); //添加箭头提示
                    //判断白棋成型了
                    if (checkSymmetryShape(false)) {
                        changeState(ActionState.REMOVE_SHAPED_WHITE);//切换到白棋快捷提子状态
                    }
                    //黑棋没成型
                    else {
                        changeState(ActionState.WAIT_FALL_BLACK);//自动交换黑棋行棋
                    }
                }
                //移动的位置有棋
                else {
                    //iGameUI.toast("只能挤到无棋的位置"); //提示用户不能这么做
                }
            }
            //挤走的位置不相邻
            else {
                //iGameUI.toast("只能挤到相邻一圈的位置"); //提示用户不能这么做
            }
        }
        //等待提走成型的黑棋
        else if (checkState(ActionState.REMOVE_SHAPED_BLACK)) {
            if (isContainsSymmetryPieces(chessInfo)) { //判断是否落子在成形区域
                for (List<ChessInfo> symmetryPiece : symmetryPieces) {
                    pieces.removeAll(symmetryPiece); //批量提子
                }
                int score = calculateSymmetryPiecesScore(); //计算得分
                blackScore += score; //加分
                if (blackScore >= maxScore) {
                    changeState(ActionState.BLACK_WIN); //黑棋获胜
                }
                else {
                    changeState(ActionState.WAIT_FALL_BLACK); //提子后继续走黑棋
                }
            }
            else {
                //iGameUI.toast("请点击成型的区域快捷提子"); //提示用户正确的操作
            }
        }
        //等待提走成型的白棋
        else if (checkState(ActionState.REMOVE_SHAPED_WHITE)) {
            if (isContainsSymmetryPieces(chessInfo)) { //判断是否落子在成形区域
                for (List<ChessInfo> symmetryPiece : symmetryPieces) {
                    pieces.removeAll(symmetryPiece); //批量提子
                }
                int score = calculateSymmetryPiecesScore(); //计算得分
                whiteScore += score; //加分
                if (whiteScore >= maxScore) {
                    changeState(ActionState.WHITE_WIN); //白棋获胜
                }
                else {
                    changeState(ActionState.WAIT_FALL_WHITE); //提子后继续走白棋
                }
            }
            else {
                //iGameUI.toast("请点击成型的区域快捷提子"); //提示用户正确的操作
            }
        }
    }

    //计算成型的棋子得分
    private int calculateSymmetryPiecesScore() {
        int score = 0;
        for (List<ChessInfo> symmetryPiece : symmetryPieces) {
            score += (symmetryPiece.size() - 5);
        }
        return score;
    }

    //判断该棋子是否在成型的棋子中
    private boolean isContainsSymmetryPieces(ChessInfo chessInfo) {
        for (List<ChessInfo> symmetryPiece : symmetryPieces) {
            if (symmetryPiece.contains(chessInfo)) return true;
        }
        return false;
    }

    //判断该位置相邻格子是否存在空格
    private boolean checkHasQi(int pCol, int pRow) {
        int maxRow = curGrids.length - 1;
        int maxCol = curGrids[0].length - 1;
        boolean hasQi = false;
        if (pRow > 0 && pCol > 0 && curGrids[pRow - 1][pCol - 1] == 0) {
            hasQi = true;
        }
        if (pRow > 0 && curGrids[pRow - 1][pCol] == 0) {
            hasQi = true;
        }
        if (pRow > 0 && pCol < maxCol && curGrids[pRow - 1][pCol + 1] == 0) {
            hasQi = true;
        }
        if (pCol > 0 && curGrids[pRow][pCol - 1] == 0) {
            hasQi = true;
        }
        if (pCol < maxCol && curGrids[pRow][pCol + 1] == 0) {
            hasQi = true;
        }
        if (pRow < maxRow && pCol > 0 && curGrids[pRow + 1][pCol - 1] == 0) {
            hasQi = true;
        }
        if (pRow < maxRow && curGrids[pRow + 1][pCol] == 0) {
            hasQi = true;
        }
        if (pRow < maxRow && pCol < maxCol && curGrids[pRow + 1][pCol + 1] == 0) {
            hasQi = true;
        }
        return hasQi;
    }

    //根据坐标点获取棋子
    private ChessInfo getChessByPoint(int pCol, int pRow) {
        for (ChessInfo piece : pieces) {
            if (piece.col == pCol && piece.row == pRow) {
                return piece;
            }
        }
        return null;
    }

    //记录落子位置
    private void setTempPoint(int pCol, int pRow) {
        tempPoint = new Point(pCol, pRow);
    }

    //判断当前状态
    private boolean checkState(ActionState state) {
        return actionState == state;
    }

    //转换状态
    private void changeState(ActionState state) {
        actionState = state;
        gameTip = state.tip;
        refreshUI();
    }

    private void refreshUI() {
        if (checkState(ActionState.WAIT_FALL_BLACK)) {
            stepAdd();;
            noticeBlackMove();
        }
        else if (checkState(ActionState.WAIT_FALL_WHITE)) {
            stepAdd();;
            noticeWhiteMove();
        }

        else if (checkState(ActionState.THEN_MOVE_BLACK)) {
            noticeAllRefreshGameInfo();
        }
        else if (checkState(ActionState.THEN_MOVE_WHITE)) {
            noticeAllRefreshGameInfo();
        }

        else if (checkState(ActionState.PUSH_AWAY_WHITE)) {
            noticeAllRefreshGameInfo();
        }
        else if (checkState(ActionState.PUSH_AWAY_BLACK)) {
            noticeAllRefreshGameInfo();
        }

        else if (checkState(ActionState.REMOVE_SHAPED_BLACK)) {
            noticeAllRefreshGameInfo();
        }
        else if (checkState(ActionState.REMOVE_SHAPED_WHITE)) {
            noticeAllRefreshGameInfo();
        }

        else if (checkState(ActionState.BLACK_WIN)) {
            stepAdd();;
            doOver(WinMode.BLACK_WIN, player_black.getNickNameOrTemp() + "执黑棋战胜" + player_white.getNickNameOrTemp());
        }
        else if (checkState(ActionState.WHITE_WIN)) {
            stepAdd();;
            doOver(WinMode.WHITE_WIN, player_white.getNickNameOrTemp() + "执白棋战胜" + player_black.getNickNameOrTemp());
        }
    }

    //在棋盘上指定坐标上落下新棋子
    private void fallPiece(boolean isBlack, int pCol, int pRow) {
        ChessInfo p = new ChessInfo(isBlack, pCol, pRow); //创建新的黑棋
        pieces.add(p); //落下黑子
    }

    //在指定坐标上添加落子提示
    private void setFallTip(int pCol, int pRow) {
        fallTip = new GroundSign(getIndex(pRow, pCol), "#00000000", ColorCost.NEW_PUT_POINT, 1.0f, 0, 0);
    }

    //指定坐标设定箭头提示
    private void setArrowTip(int pCol, int pRow) {
        arrowTip = new LineSign(getIndex(tempPoint.y, tempPoint.x), getIndex(pRow, pCol), ColorCost.MOVE_ARROW, 1);
    }

    //检查是否形成了对称图形
    private boolean checkSymmetryShape(boolean isBlackChess) {
        boolean isSymmetryShaped = false;
        symmetryPieces.clear();
        List<ChessInfo> iChess = new ArrayList<>(pieces);
        iChess.removeIf(chessInfo -> chessInfo.isBlack != isBlackChess); //保留同色棋子集合
        Map<ChessInfo, Set<ChessInfo>> adjacentGroup = new HashMap<>(); //相邻棋子组成的组
        for (ChessInfo key : iChess) { //每个棋子都建立一个自己的组
            Set<ChessInfo> set = new HashSet<>();
            set.add(key);
            adjacentGroup.put(key, set);
        }
        int size = iChess.size();
        for (int i = 0; i < size; i++) { //第一次遍历所有相邻棋子建立相邻关系
            for (int j = i + 1; j < size; j++) { //保证任意两个棋子只判断一次
                ChessInfo a = iChess.get(i);
                ChessInfo b = iChess.get(j);
                if (isAdjacent(a, b)) {
                    adjacentGroup.get(a).add(b);
                    adjacentGroup.get(b).add(a);
                }
            }
        }
        //printAdjacentGroup("第一次建立关系", adjacentGroup);
        Map<ChessInfo, Set<ChessInfo>> blockGroup = new HashMap<>();
        for (int i = 0; i < size; i++) { //第二次遍历所有邻组合并
            ChessInfo a = iChess.get(i);//因为adjacentGroup是map没有顺序索引所以借助iChess集合来遍历
            Set<ChessInfo> aSet = adjacentGroup.get(a);
            for (ChessInfo blockKey : blockGroup.keySet()) {  //判断这一组是否和已经有的块有交集，保证块不重复
                Set<ChessInfo> blockSet = blockGroup.get(blockKey);
                if (isIntersection(aSet, blockSet)) { //如果有的话
                    a = blockKey;   //就还是用最早的键来判断
                    blockSet.addAll(aSet); //且把这一组的所有棋子添加到块中
                    aSet = blockSet; //且用最全的集合来判断
                }
            }
            for (int j = i + 1; j < size; j++) {
                ChessInfo b = iChess.get(j);
                Set<ChessInfo> bSet = adjacentGroup.get(b);
                if (isIntersection(aSet, bSet)) {
                    aSet.addAll(bSet); //判断过有交集的都要收集起来以免后面的漏判
                    blockGroup.put(a, aSet); //这里的a始终是最早的块键
                }
            }
        }
        //printAdjacentGroup("合并邻组后", blockGroup);
        Map<ChessInfo, Set<ChessInfo>> singleGroup = new HashMap<>();//此时可能出现来自不同相邻关系组成的相同集合的组，将其净化掉
        for (ChessInfo blockKey : blockGroup.keySet()) {
            Set<ChessInfo> blockSet = blockGroup.get(blockKey);
            boolean isSetEqual = false;
            for (ChessInfo singleKey : singleGroup.keySet()) {
                Set<ChessInfo> singleSet = singleGroup.get(singleKey);
                isSetEqual = isSetEqual(blockSet, singleSet);
                if (isSetEqual) {  //一旦存在相同就不要继续比较了，这组不加到singleGroup中
                    break;
                }
            }
            if (!isSetEqual) {
                singleGroup.put(blockKey, blockSet);
            }
        }
        //printAdjacentGroup("去除相同组后", singleGroup);
        for (ChessInfo key : singleGroup.keySet()) {
            Set<ChessInfo> singleSet = singleGroup.get(key);
            if (singleSet.size() >= 6) { //棋子数量小于6的块都不去考虑了
                int minCol = -1, minRow = -1, maxCol = -1, maxRow = -1;
                for (ChessInfo chessInfo : singleSet) { //获取区间范围
                    if (minCol == -1) { //注意第一次赋予初值
                        minCol = chessInfo.col;
                        maxCol = chessInfo.col;
                        minRow = chessInfo.row;
                        maxRow = chessInfo.row;
                    }
                    if (chessInfo.col > maxCol) {
                        maxCol = chessInfo.col;
                    }
                    if (chessInfo.col < minCol) {
                        minCol = chessInfo.col;
                    }
                    if (chessInfo.row > maxRow) {
                        maxRow = chessInfo.row;
                    }
                    if (chessInfo.row < minRow) {
                        minRow = chessInfo.row;
                    }
                }
                float midCol = (minCol + maxCol) / 2f; //获取中心点横坐标，用于后续计算
                float midRow = (minRow + maxRow) / 2f; //获取中心点纵坐标，用于后续计算
                if (checkHorizontalSymmetry(singleSet, midCol)) {
                    isSymmetryShaped = true;
                    symmetryPieces.add(new ArrayList<>(singleSet));
                }
                else if (checkVerticalSymmetry(singleSet, midRow)) {
                    isSymmetryShaped = true;
                    symmetryPieces.add(new ArrayList<>(singleSet));
                }
                else if (checkPositiveSymmetry(singleSet, midCol, midRow)) {
                    isSymmetryShaped = true;
                    symmetryPieces.add(new ArrayList<>(singleSet));
                }
                else if (checkNegativeSymmetry(singleSet, midCol, midRow)) {
                    isSymmetryShaped = true;
                    symmetryPieces.add(new ArrayList<>(singleSet));
                }
            }
        }
        return isSymmetryShaped;
    }

    //判断是否水平对称无斜率（一条竖线两边全等）
    private boolean checkHorizontalSymmetry(Set<ChessInfo> blockChessSet, float midCol) {
        Set<Point> blockSet = new HashSet<>();
        for (ChessInfo chessInfo : blockChessSet) {
            blockSet.add(new Point(chessInfo.col, chessInfo.row));
        }
        Set<Point> left = new HashSet<>();
        Set<Point> right = new HashSet<>();
        for (Point chessInfo : blockSet) {
            if (chessInfo.x < midCol) {
                left.add(chessInfo);
                float diff = midCol - chessInfo.x; //计算要翻转的值
                chessInfo.x = (int) (midCol + diff); //翻转坐标
            }
            else if (chessInfo.x > midCol) {
                right.add(chessInfo);
            }
        }
        boolean isFlag = isSameCoordinate(left, right); //判断两组棋子横纵坐标是否全等
        return isFlag;
    }

    //判断是否垂直对称（一条横线两边全等）
    private boolean checkVerticalSymmetry(Set<ChessInfo> blockChessSet, float midRow) {
        Set<Point> blockSet = new HashSet<>();
        for (ChessInfo chessInfo : blockChessSet) {
            blockSet.add(new Point(chessInfo.col, chessInfo.row));
        }
        Set<Point> top = new HashSet<>();
        Set<Point> bottom = new HashSet<>();
        for (Point chessInfo : blockSet) {
            if (chessInfo.y < midRow) {
                top.add(chessInfo);
                float diff = midRow - chessInfo.y; //计算要翻转的值
                chessInfo.y = (int) (midRow + diff); //翻转坐标
            }
            else if (chessInfo.y > midRow) {
                bottom.add(chessInfo);
            }
        }
        boolean isFlag = isSameCoordinate(top, bottom);
        return isFlag;
    }

    //判断是否以45°的斜线对称
    private boolean checkPositiveSymmetry(Set<ChessInfo> blockChessSet, float midCol, float midRow) {
        Set<Point> blockSet = new HashSet<>();
        for (ChessInfo chessInfo : blockChessSet) {
            blockSet.add(new Point(chessInfo.col, chessInfo.row));
        }
        Set<Point> leftTop = new HashSet<>();
        Set<Point> rightBottom = new HashSet<>();
        float split = midCol + midRow;
        for (Point chessInfo : blockSet) {
            if (chessInfo.y + chessInfo.x < split) {
                leftTop.add(chessInfo);
                //先求两条直线的交点
                float b = midCol + midRow; //根据公式y = ax + b;a=-1; 求对称线的公式需要的参数
                float h = chessInfo.y - chessInfo.x; //求对齐线的公式需要的参数
                float jX = (b - h) / 2; //求出垂线交点x
                float jY = (b + h) / 2;//求出垂线交点y
                float diffX = jX - chessInfo.x;
                chessInfo.x = (int) (jX + diffX);
                float diffY = jY - chessInfo.y;
                chessInfo.y = (int) (jY + diffY);
            }
            else if (chessInfo.y + chessInfo.x > split) {
                rightBottom.add(chessInfo);
            }
        }
        boolean isFlag = isSameCoordinate(leftTop, rightBottom);
        return isFlag;
    }

    //判断是否以-45°的斜线对称
    private boolean checkNegativeSymmetry(Set<ChessInfo> blockChessSet, float midCol, float midRow) {
        Set<Point> blockSet = new HashSet<>();
        for (ChessInfo chessInfo : blockChessSet) {
            blockSet.add(new Point(chessInfo.col, chessInfo.row));
        }
        Set<Point> left = new HashSet<>();
        Set<Point> right = new HashSet<>();
        for (Point chessInfo : blockSet) {  //先全部水平翻转，这样midCol和midRow不会变，而变成可以以正45°斜线对称了
            if (chessInfo.x < midCol) {
                left.add(chessInfo);
                float diff = midCol - chessInfo.x;
                chessInfo.x = (int) (midCol + diff);
            }
            else if (chessInfo.x > midCol) {
                right.add(chessInfo);
                float diff = chessInfo.x - midCol;
                chessInfo.x = (int) (midCol - diff); //还原改变
            }
        }
        Set<Point> leftTop = new HashSet<>();
        Set<Point> rightBottom = new HashSet<>();
        float split = midCol + midRow;
        for (Point chessInfo : blockSet) {
            if (chessInfo.y + chessInfo.x < split) {
                leftTop.add(chessInfo);
                float b = midCol + midRow; //根据公式y = ax + b;a=-1; 求对称线的公式需要的参数
                float h = chessInfo.y - chessInfo.x; //求对齐线的公式需要的参数
                float jX = (b - h) / 2; //求出垂线交点x
                float jY = (b + h) / 2;//求出垂线交点y
                float diffX = jX - chessInfo.x;
                chessInfo.x = (int) (jX + diffX);
                float diffY = jY - chessInfo.y;
                chessInfo.y = (int) (jY + diffY);
            }
            else if (chessInfo.y + chessInfo.x > split) {
                rightBottom.add(chessInfo);
            }
        }
        boolean isFlag = isSameCoordinate(leftTop, rightBottom);
        return isFlag;
    }

    //打印邻组，邻组是我算法里的一个中间概念
    private void printAdjacentGroup(String title, Map<?, Set<ChessInfo>> adjacentGroup) {
        int row = curGrids.length;
        //System.out.println(title + "有" + adjacentGroup.size() + "组");
        for (Object key : adjacentGroup.keySet()) {
            ChessInfo keyChess = (ChessInfo) key;
            String colStr = String.valueOf((char) ('A' + keyChess.col));
            String rowStr = String.valueOf(row - keyChess.row);
            //System.out.print(colStr + rowStr + "组 ");
            for (ChessInfo chessInfo : adjacentGroup.get(key)) {
                String colStr2 = String.valueOf((char) ('A' + chessInfo.col));
                String rowStr2 = String.valueOf(row - chessInfo.row);
                //System.out.print(colStr2 + rowStr2 + "；");
            }
            //System.out.println("。");
        }
    }

    //判断两个棋子是否相邻(直向和斜向相邻都算,位置相同不算)
    private boolean isAdjacent(ChessInfo a, ChessInfo b) {
        if (a.col == b.col && a.row == b.row) {
            return false;
        }
        else {
            return Math.abs(a.col - b.col) <= 1 && Math.abs(a.row - b.row) <= 1;
        }
    }

    //判断两组棋子横纵坐标是否全等
    private boolean isSameCoordinate(Set<Point> aSet, Set<Point> bSet) {
        if (aSet.size() != bSet.size()) {
            return false;
        }
        else {
            for (Point chessInfo : aSet) {
                boolean same = false;
                for (Point info : bSet) {
                    if (info.y == chessInfo.y && info.x == chessInfo.x) {
                        same = true;
                        break;
                    }
                }
                if (!same) {
                    return false;
                }
            }
        }
        return true;
    }

    //判断两个邻组是否有交集（只要有一个棋子双方都存在就算有交集）
    private boolean isIntersection(Set<ChessInfo> aSet, Set<ChessInfo> bSet) {
        Set<ChessInfo> resSet = new HashSet<>(aSet);
        resSet.retainAll(bSet); //++这个可能无法判断出来顺序不一致的情况
        return resSet.size() > 0;
    }

    //判断两个集合是否相同
    public boolean isSetEqual(Set<ChessInfo> aSet, Set<ChessInfo> bSet) {
        int a = aSet.size();
        int b = bSet.size();
        if (a == b) {
            Set<ChessInfo> resSet = new HashSet<>();
            resSet.addAll(aSet);
            resSet.addAll(bSet);
            int c = resSet.size();
            return c == a;
        }
        else {
            return false;
        }
    }

    private static class ChessInfo {

        public boolean isBlack;
        public int col;
        public int row;

        public ChessInfo(boolean isBlack, int col, int row) {
            this.isBlack = isBlack;
            this.col = col;
            this.row = row;
        }
    }

    private enum ActionState {
        /**
         * 等待黑棋落子
         */
        WAIT_FALL_BLACK("等待黑棋落子..."),
        /**
         * 等待白棋落子
         */
        WAIT_FALL_WHITE("等待白棋落子..."),

        /**
         * 黑棋落黑棋后移动
         */
        THEN_MOVE_BLACK("选择黑棋移动的位置..."),
        /**
         * 白棋落白棋后移动
         */
        THEN_MOVE_WHITE("选择白棋移动的位置..."),

        /**
         * 选择被黑棋挤走的白棋的位置
         */
        PUSH_AWAY_WHITE("选择白棋被挤到的位置..."),
        /**
         * 选择被白棋挤走的黑棋的位置
         */
        PUSH_AWAY_BLACK("选择黑棋被挤到的位置..."),

        /**
         * 一键提走成型的黑棋（提子后算分）
         */
        REMOVE_SHAPED_BLACK("点击黑棋成型的区域快捷提子..."),
        /**
         * 一键提走成型的白棋（提子后算分）
         */
        REMOVE_SHAPED_WHITE("点击白棋成型的区域快捷提子..."),

        /**
         * 对弈结束，黑方获胜，棋盘不再接受事件
         */
        BLACK_WIN("恭喜黑棋获胜！"),
        /**
         * 对弈结束，白方获胜，棋盘不再接受事件
         */
        WHITE_WIN("恭喜白棋获胜！");

        /**
         * 该状态对应的操作提示
         */
        public final String tip;

        ActionState(String tip) {
            this.tip = tip;
        }
    }

}
